package thinkingInJava.io;
// page 566 对比allocate 与 allocateDirect
//: io/E25_AllocateDirect.java
//{RunByHand}
/******************** Exercise 25 *************************
 * Experiment with changing the ByteBuffer.allocate()
 * statements in the examples in this chapter to
 * ByteBuffer.allocateDirect(). Demonstrate performance
Thinking in Java, 4 th Edition Annotated Solution Guide  526
 * differences, but also notice whether the startup time
 * of the programs noticeably changes.
 *********************************************************/
import java.nio.*;
import java.nio.channels.*;
import java.io.*;
import java.nio.charset.*;
abstract class CompareAllocations {
	private String name;
	protected ByteBuffer buffer;
	private int size;
	public CompareAllocations(String name, int size) {
		this.name = name;
		this.size = size;
	}
	public void runComparison() {
		System.out.println("Program Name: <" + name + ">");
		try {
			long startTime = System.nanoTime();
			directAllocate();
			long endTime = System.nanoTime();
			System.out.println(
					"Direct Allocation Cost for buffer of size: <"
							+ size + "> is <" + (endTime - startTime) + ">");
			startTime = System.nanoTime();
			execute();
			endTime = System.nanoTime();
			System.out.println(
					"Execution cost using direct buffer: <"
							+ (endTime - startTime) + ">");
			startTime = System.nanoTime();
			indirectAllocate();
			endTime = System.nanoTime();
			System.out.println(
					"Indirect Allocation Cost for buffer of size: <"
							+ size + "> is <" + (endTime - startTime) + ">");
			startTime = System.nanoTime();
			execute();
			endTime = System.nanoTime();
			System.out.println(
					"Execution cost using indirect buffer: <"
							+ (endTime - startTime) + ">");
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	public void directAllocate() {
		buffer = ByteBuffer.allocateDirect(size);
	}
	public abstract void execute() throws IOException;
	public void indirectAllocate() {
		buffer = ByteBuffer.allocate(size);
	}
}
public class E25_AllocateDirect {
	public static void main(String[] args) {
		CompareAllocations[] comparisons = {
				new CompareAllocations("GetChannel", 8192) {
					public void execute() throws IOException {
						FileChannel fc =
								new FileInputStream("src/test/io/E25_AllocateDirect.java")
								.getChannel();
						fc.read(buffer);
						buffer.flip();
						while(buffer.hasRemaining())
							buffer.get();
					}
				},
				new CompareAllocations("ChannelCopy", 16384) {
					public void execute() throws IOException {
						FileChannel in =
								new FileInputStream("src/test/io/E25_AllocateDirect.java")
								.getChannel(),
								out = new FileOutputStream("temp.txt")
								.getChannel();
						while(in.read(buffer) != -1) {
							buffer.flip(); // Prepare for writing
							out.write(buffer);
							buffer.clear(); // Prepare for reading
						}
					}
				},
				new CompareAllocations("BufferToText", 8192) {
					public void execute() throws IOException {
						FileChannel fc =
								new FileOutputStream("data2.txt")
								.getChannel();
						fc.write(ByteBuffer.wrap("Some text".getBytes()));
						fc.close();
						fc = new FileInputStream("data2.txt")
								.getChannel();
						fc.read(buffer);
						buffer.flip();
						buffer.asCharBuffer().toString();
						//Decode using this system's default Charset:
						buffer.rewind();
						Charset.forName(
								System.getProperty("file.encoding"))
						.decode(buffer);
						fc = new FileOutputStream("data2.txt")
								.getChannel();
						fc.write(ByteBuffer.wrap(
								"Some text".getBytes("UTF-16BE")));
						fc.close();
						//Now try reading again:
						fc = new FileInputStream("data2.txt")
								.getChannel();
						buffer.clear();
						fc.read(buffer);
						buffer.flip();
						buffer.asCharBuffer().toString();
						//Use a CharBuffer to write through:
						fc = new FileOutputStream("data2.txt")
								.getChannel();
						buffer.clear();
						buffer.asCharBuffer().put("Some text");
						fc.write(buffer);
						fc.close();
						//Read and display:
						fc = new FileInputStream("data2.txt")
								.getChannel();
						buffer.clear();
						fc.read(buffer);
						buffer.flip();
						buffer.asCharBuffer().toString();
					}
				},
				new CompareAllocations("GetData", 1024) {
					public void execute() throws IOException {
						//Store and read a char array:
						buffer.asCharBuffer().put("Howdy!");
						//Store and read a short:
						buffer.asShortBuffer().put((short)471142);
						buffer.getShort();
						buffer.rewind();
						//Store and read an int:
						buffer.asIntBuffer().put(99471142);
						buffer.getInt();
						buffer.rewind();
						//Store and read a long:
						buffer.asLongBuffer().put(99471142);
						buffer.getLong();
						buffer.rewind();
						//Store and read a float:
						buffer.asFloatBuffer().put(99471142);
						buffer.getFloat();
						buffer.rewind();
						//Store and read a double:
						buffer.asDoubleBuffer().put(99471142);
						buffer.getDouble();
						buffer.rewind();
					}
				},
				new CompareAllocations("IntBufferDemo", 1024) {
					public void execute() throws IOException {
						IntBuffer ib = buffer.asIntBuffer();
						//Store an array of int:
						ib.put(
								new int[] { 11, 42, 47, 99, 143, 811, 1016 });
						//Absolute location read and write:
						ib.get(3);
						ib.put(3, 1811);
						ib.flip();
						while(ib.hasRemaining()) {
							int i = ib.get();
						}
					}
				},
				new CompareAllocations("UsingBuffers", 32) {
					public void execute() throws IOException {
						char[] data = "UsingBuffers".toCharArray();
						CharBuffer cb = buffer.asCharBuffer();
						cb.put(data);
						cb.rewind();
						symmetricScramble(cb);
						cb.rewind();
						symmetricScramble(cb);
						cb.rewind();
					}
					private void symmetricScramble(CharBuffer buffer) {
						while(buffer.hasRemaining()) {
							buffer.mark();
							char c1 = buffer.get();
							char c2 = buffer.get();
							buffer.reset();
							buffer.put(c2).put(c1);
						}
					}
				}
		};
		for(int i = 0; i < comparisons.length; i++)
			comparisons[i].runComparison();
	}
} ///:~
//Direct buffers increase program performance in bulk operations, but typically
//have somewhat higher allocation and deallocation costs than non-direct buffers
//http://blog.sina.com.cn/s/blog_67dc11000101cpsk.html
//为什么要提供两种方式呢？这与Java的内存使用机制有关。allocate产生的内存开销是在JVM中的，
//而allocateDirect产生的开销在JVM之外，以就是系统级的内存分配。当Java程序接收到外部传来的数据时，
//首先是被系统内存所获取，然后在由系统内存复制拷贝到JVM内存中供Java程序使用。所以在allocateDirect，
//可以省去复制这一步操作，效率上会有所提高。但是系统级内存的分配比起JVM内存的分配要耗时得多，
//所以并不是任何时候allocateDirect的操作效率都是最高的。下面是一个不同容量情况下两种分配方式的操作时间对比：
//由图可以看出，当操作数据量很小时，两种分配方式操作使用时间基本是相同的，allocate有时可能会更快，
//但是当数据量很大时，allocateDirect方式会远远大于第一种的分配方式。